/*	$Id: dlink_pcap.c 6 2008-07-29 02:30:10Z phrakt $	*/
/*
 * Copyright (c) 2003 Jean-Francois Brousseau <jfb@openbsd.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * Datalink abstraction layer for libpcap
 *
 * This file contains code to provide an abstraction layer between a file
 * containing libpcap-compatible packet dumps and the Packet Forge datalink
 * interface.  When using this layer, reads will return successive packets
 * from the pcap dump file.  Writes will be saved in another file opened
 * for output.
 *
 * Before any read or write operation is allowed on a pcap datalink object,
 * the appropriate file paths must be given through the PFDL_CTL_SINFILE and
 * PFDL_CTL_SOUTFILE, respectively.
 */

#include <sys/param.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#include <net/if.h>

#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include "if.h"
#include "dlink.h"
#include "pforge.h"


int      dlink_pcap_open  (PF_DLINK *);
int      dlink_pcap_close (PF_DLINK *);
int      dlink_pcap_bind  (PF_DLINK *, int);
int      dlink_pcap_ctl   (PF_DLINK *, int, void *);
PF_PKT*  dlink_pcap_read  (PF_DLINK *);
ssize_t  dlink_pcap_write (PF_DLINK *, void *, size_t);


struct pf_dlink_ops dlink_pcap_ops = {
	dlink_pcap_open,
	dlink_pcap_close,
	dlink_pcap_bind,
	dlink_pcap_ctl,
	dlink_pcap_read,
	dlink_pcap_write
};

struct dlink_pcap_data {
	char             infile[MAXPATHLEN];
	char             outfile[MAXPATHLEN];
	PF_PCAP		*pcap_in;
	PF_PCAP		*pcap_out;
	PF_NETIF	*iface;
};

/*
 * dlink_pcap_open()
 *
 */
int
dlink_pcap_open(PF_DLINK *dl)
{
	char dev[PFIF_NAME_MAXLEN];
	int i;

	dl->dldata = malloc(sizeof(struct dlink_pcap_data));
	if (dl->dldata == NULL) {
		return (-1);
	}
	memset(dl->dldata, 0, sizeof(struct dlink_pcap_data));

	return (0);
}


/*
 * dlink_pcap_close()
 *
 * Close the pcap savefile handle.
 */
int
dlink_pcap_close(PF_DLINK *dl)
{
	struct dlink_pcap_data *data = (struct dlink_pcap_data *)dl->dldata;

	if (data != NULL) {
		if (data->pcap_in != NULL)
			pf_pcap_close(data->pcap_in);
		if (data->pcap_out != NULL)
			pf_pcap_close(data->pcap_out);
		free(data);
		dl->dldata = NULL;
	}

	dl->dldev[0] = '\0';
	return (0);
}


/*
 * dlink_pcap_bind()
 */
int
dlink_pcap_bind(PF_DLINK *dl, int nid)
{
	int major, minor;
	PF_PCAP *pcap;
	struct dlink_pcap_data *data = (struct dlink_pcap_data *)dl->dldata;

	pcap = pf_pcap_open(data->infile, O_RDONLY);
	if (pcap == NULL) {
		return (-1);
	}

	data->pcap_in = pcap;
	return (0);
}


/*
 * dlink_pcap_ctl()
 *
 * Perform various control operations on the PCAP datalink abstraction layer.
 * Returns 0 on success, or -1 on failure.
 */
int
dlink_pcap_ctl(PF_DLINK *dl, int type, void *data)
{
	char *str;
	struct dlink_pcap_data *pcap_data;

	pcap_data = (struct dlink_pcap_data *)dl->dldata;

	switch (type) {
	case PFDL_CTL_SINFILE:
		str = (char *)data;
		if (strlcpy(pcap_data->infile, str,
		    sizeof(pcap_data->infile)) >= sizeof(pcap_data->infile)) {
			return (-1);
		}
	case PFDL_CTL_GINFILE:
	case PFDL_CTL_SOUTFILE:
	case PFDL_CTL_GOUTFILE:
		break;
	default:
		pf_seterr(PF_ERR_NOTSUP);
		return (-1);
	}

	return (0);
}


/*
 * dlink_pcap_read()
 *
 * Read a packet from the pcap input file.
 */
PF_PKT*
dlink_pcap_read(PF_DLINK *dl)
{
	PF_PKT *pkt;
	struct dlink_pcap_data *data = (struct dlink_pcap_data *)dl->dldata;

	if (data->infile[0] == '\0') {
		pf_seterr(PF_ERR_INERR);
		return (NULL);
	}

	pkt = pf_pcap_readpkt(data->pcap_in);
	if (pkt == NULL) {
		return (NULL);
	}

	return (pkt);
}

/*
 * dlink_pcap_write()
 *
 * Write a packet to the datalink layer.
 */
ssize_t
dlink_pcap_write(PF_DLINK *dl, void *src, size_t len)
{
	struct dlink_pcap_data *data;

	data = (struct dlink_pcap_data *)dl->dldata;

	if (data->outfile[0] == '\0') {
		pf_seterr(PF_ERR_OUTERR);
		return (-1);
	}

	return (0);
}
